package org.example.myleet.p391;

import java.util.*;

/**
 * 官方解：
 * 先看面积，小矩形面积加起来一定要等于[minX, minY]、[maxX, maxY]组成的大矩形的面积，否则就是有缺陷的，或重叠过多
 * 还有一种比较特殊的情况，就是有重叠，但面积刚好等于大矩形的面积
 * 此时需要考虑小矩形顶点出现的次数，归纳可知完美矩形中小矩形的顶点数除了大矩形四个角分别只有一个之外，其他顶点出现的计数要么是2要么是4
 * 取巧办法，找到四个角之后再加入一次计数中使得有效的顶点计数要么是2要么是4
 */
public class Solution {
    public boolean isRectangleCover(int[][] rectangles) {
        int n = rectangles.length;
        if (n == 1) {
            return true;
        }
        Map<Point, Integer> pointsCount = new HashMap<>();
        int[] rectangle = rectangles[0];
        int sumArea = (rectangle[2] - rectangle[0]) * (rectangle[3] - rectangle[1]), minX = rectangle[0], minY = rectangle[1], maxX = rectangle[2], maxY = rectangle[3];
        Point p1 = new Point(rectangle[0], rectangle[1]);
        pointsCount.put(p1, pointsCount.getOrDefault(p1, 0) + 1);
        Point p2 = new Point(rectangle[2], rectangle[1]);
        pointsCount.put(p2, pointsCount.getOrDefault(p2, 0) + 1);
        Point p3 = new Point(rectangle[2], rectangle[3]);
        pointsCount.put(p3, pointsCount.getOrDefault(p3, 0) + 1);
        Point p4 = new Point(rectangle[0], rectangle[3]);
        pointsCount.put(p4, pointsCount.getOrDefault(p4, 0) + 1);
        for (int i = 1; i < n; ++i) {
            rectangle = rectangles[i];
            minX = Math.min(minX, rectangle[0]);
            minY = Math.min(minY, rectangle[1]);
            maxX = Math.max(maxX, rectangle[2]);
            maxY = Math.max(maxY, rectangle[3]);
            sumArea += (rectangle[2] - rectangle[0]) * (rectangle[3] - rectangle[1]);
            p1 = new Point(rectangle[0], rectangle[1]);
            pointsCount.put(p1, pointsCount.getOrDefault(p1, 0) + 1);
            p2 = new Point(rectangle[2], rectangle[1]);
            pointsCount.put(p2, pointsCount.getOrDefault(p2, 0) + 1);
            p3 = new Point(rectangle[2], rectangle[3]);
            pointsCount.put(p3, pointsCount.getOrDefault(p3, 0) + 1);
            p4 = new Point(rectangle[0], rectangle[3]);
            pointsCount.put(p4, pointsCount.getOrDefault(p4, 0) + 1);
        }
        if (sumArea != (maxX - minX) * (maxY - minY)) {
            return false;
        }
        p1 = new Point(minX, minY);
        pointsCount.put(p1, pointsCount.getOrDefault(p1, 0) + 1);
        p2 = new Point(maxX, minY);
        pointsCount.put(p2, pointsCount.getOrDefault(p2, 0) + 1);
        p3 = new Point(maxX, maxY);
        pointsCount.put(p3, pointsCount.getOrDefault(p3, 0) + 1);
        p4 = new Point(minX, maxY);
        pointsCount.put(p4, pointsCount.getOrDefault(p4, 0) + 1);
        for (Map.Entry<Point, Integer> entry : pointsCount.entrySet()) {
            if (entry.getValue() == 2 || entry.getValue() == 4) {
                //OK
            } else {
                return false;
            }
        }
        return true;
    }

    static class Point {
        public int x;
        public int y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("Point{");
            sb.append("x=").append(x);
            sb.append(", y=").append(y);
            sb.append('}');
            return sb.toString();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Point point = (Point) o;
            return x == point.x &&
                    y == point.y;
        }

        @Override
        public int hashCode() {
            return Objects.hash(x, y);
        }
    }
}
